package com.reformer.commons.httpclient;

import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class HttpClientUtil {

    /**
     * 发送 post请求访问本地应用并根据传递参数不同返回不同结果
     *
     * @param url    请求地址
     * @param params 请求参数
     * @return
     * @throws Exception
     */
    public static String postForm(String url, List<NameValuePair> params) throws Exception {
        return postForm(url, params, null);
    }

    /**
     * 表单请求
     * @param url
     * @param params
     * @param headers 请求头
     * @return
     * @throws Exception
     */
    public static String postForm(String url, List<NameValuePair> params, Header[] headers) throws Exception {
        // 创建httppost
        HttpPost httppost = new HttpPost(url);
        UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(params, "UTF-8");
        if (params != null && params.size() > 0) {
            httppost.setEntity(uefEntity);
        }
        if (headers != null) {
            httppost.setHeaders(headers);
        }
        return getResult(httppost);
    }

    /**
     * post表单请求，可通过返回对象获取响应头和
     * @param url
     * @param params
     * @param headers
     * @return
     * @throws Exception
     */
    public static HttpResponse postFormAndResultHttpResponse(String url, List<NameValuePair> params, Header[] headers) throws Exception {
        // 创建httppost
        HttpPost httppost = new HttpPost(url);
        UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(params, "UTF-8");
        if (params != null && params.size() > 0) {
            httppost.setEntity(uefEntity);
        }
        if (headers != null) {
            httppost.setHeaders(headers);
        }
        return getHttpResponse(httppost);
    }

    public static HttpResponse postFormAndResultHttpResponse(String url, Map params, Header[] headers) throws Exception {
        return postFormAndResultHttpResponse(url, formatNVP(params), headers);
    }

    public static HttpResponse postFormAndResultHttpResponse(String url, Map params) throws Exception {
        return postFormAndResultHttpResponse(url, formatNVP(params), null);
    }

    /**
     * post请求，类似于form表单形式提交
     *
     * @param url
     * @param map
     * @return
     * @throws Exception
     */
    public static String postForm(String url, Map map) throws Exception {
        return postForm(url, formatNVP(map), null);
    }

    /**
     * 发送 post请求访问本地应用并根据传递参数不同返回不同结果
     *
     * @param url         请求地址
     * @param str         请求内容
     * @param contentType 请求内容的类型
     * @return
     * @throws Exception
     */
    public static String postNotForm(String url, String str, ContentType contentType) throws Exception {
        return postNotForm(url, str, contentType, null);
    }

    /**
     * post请求
     *
     * @param url         请求地址
     * @param str         请求内容
     * @param contentType 请求内容的类型
     * @param headers     请求头
     * @return
     * @throws Exception
     */
    public static String postNotForm(String url, String str, ContentType contentType, Header[] headers) throws Exception {
        // 创建httppost
        HttpPost httppost = new HttpPost(url);
        if (str != null && str.trim().length() > 0) {
            System.out.println("Request content:" + str);
            httppost.setEntity(new StringEntity(str, contentType));
        }
        if (headers != null) {
            httppost.setHeaders(headers);
        }
        return getResult(httppost);
    }

    /**
     * 发送 get请求
     *
     * @param url    请求地址
     * @param params 请求参数
     * @return
     * @throws Exception
     */
    public static String get(String url, List<NameValuePair> params) throws Exception {
        return get(url, params, null);
    }

    /**
     * get请求
     *
     * @param url
     * @return
     * @throws Exception
     */
    public static String get(String url) throws Exception {
        return get(url, new ArrayList<NameValuePair>(), null);
    }

    /**
     * get请求
     *
     * @param url
     * @param headers
     * @return
     * @throws Exception
     */
    public static String get(String url, Header[] headers) throws Exception {
        return get(url, new ArrayList<NameValuePair>(), headers);
    }

    /**
     * get请求
     *
     * @param url     请求地址
     * @param params  请求参数
     * @param headers 请求头
     * @return
     * @throws Exception
     */
    public static String get(String url, List<NameValuePair> params, Header[] headers) throws Exception {
        // 创建httpget.
        HttpGet httpget = new HttpGet(url);
        UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(params, "UTF-8");
        if (params != null && params.size() > 0) {
            // 设置参数
            String str = EntityUtils.toString(new UrlEncodedFormEntity(params));
            httpget.setURI(new URI(httpget.getURI().toString() + "?" + str));
        }
        if (headers != null) {
            httpget.setHeaders(headers);
        }
        return getResult(httpget);
    }

    /**
     * get请求
     *
     * @param url
     * @param map 请求参数
     * @return
     * @throws Exception
     */
    public static String get(String url, Map map) throws Exception {
        return get(url, formatNVP(map), null);
    }

    /**
     * get请求
     *
     * @param url
     * @param map
     * @param headers
     * @return
     * @throws Exception
     */
    public static String get(String url, Map map, Header[] headers) throws Exception {
        return get(url, formatNVP(map), headers);
    }

    /**
     * 上传文件
     *
     * @param url      请求地址
     * @param filepath 文件地址（本地地址）
     * @param params   请求参数
     * @return
     * @throws Exception
     */
    public static String upload(String url, String filepath, List<NameValuePair> params) throws Exception {
        HttpPost httppost = new HttpPost(url);
        FileBody fileBody = new FileBody(new File(filepath));
        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
        multipartEntityBuilder.addPart("file", fileBody);
        if (params != null && params.size() > 0) {
            for (Iterator it = params.iterator(); it.hasNext(); ) {
                NameValuePair pair = (NameValuePair) it.next();
                multipartEntityBuilder.addPart(pair.getName(), new StringBody(pair.getValue(), ContentType.APPLICATION_OCTET_STREAM/*ContentType.create("text/html", Consts.UTF_8)*/));
            }
        }
        HttpEntity reqEntity = multipartEntityBuilder.build();
        httppost.setEntity(reqEntity);
        return getResult(httppost);
    }

    public static String upload(String url, String localFile, String savePath) throws Exception {
        return upload(url, new File(localFile), savePath);
    }

    public static String upload(String url, File localFile, String savePath) throws Exception {
        HttpPost httppost = new HttpPost(url);
        FileBody fileBody = new FileBody(localFile);
        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
        multipartEntityBuilder.addPart("file", fileBody).addPart("path", new StringBody(savePath, ContentType.APPLICATION_OCTET_STREAM));
        HttpEntity reqEntity = multipartEntityBuilder.build();
        httppost.setEntity(reqEntity);
        return getResult(httppost);
    }

    /**
     * 上传文件
     *
     * @param url
     * @param filepath
     * @param map
     * @return
     * @throws Exception
     */
    public static String upload(String url, String filepath, Map map) throws Exception {
        return upload(url, filepath, formatNVP(map));
    }

    /**
     * 文件上传
     *
     * @param url
     * @param filename 文件名
     * @param in       文件输入流
     * @param params   请求参数
     * @return
     * @throws Exception
     */
    public static String upload(String url, String filename, InputStream in, List<NameValuePair> params) throws Exception {
        if (in != null) {
            try {
                HttpPost httppost = new HttpPost(url);
                InputStreamBody body = new InputStreamBody(in, filename);
                MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
                multipartEntityBuilder.addPart("file", body);
                if (params != null && params.size() > 0) {
                    for (Iterator it = params.iterator(); it.hasNext(); ) {
                        NameValuePair pair = (NameValuePair) it.next();
                        multipartEntityBuilder.addPart(pair.getName(), new StringBody(pair.getValue(), ContentType.create("text/html", Consts.UTF_8)));
                    }
                }
                HttpEntity reqEntity = multipartEntityBuilder.build();
                httppost.setEntity(reqEntity);
                return getResult(httppost);
            } finally {
                in.close();
            }
        } else {
            throw new NullPointerException("上传文件输入流不能为空");
        }
    }

    public static String upload(String url, String filename, InputStream in, Map map) throws Exception {
        return upload(url, filename, formatNVP(map));
    }

    public static String upload(String url, String filename, InputStream in, String savePath) throws Exception {
        if (in != null) {
            try {
                HttpPost httppost = new HttpPost(url);
                InputStreamBody body = new InputStreamBody(in, filename);
                MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
                multipartEntityBuilder.addPart("file", body).addPart("path", new StringBody(savePath, ContentType.APPLICATION_OCTET_STREAM));
                HttpEntity reqEntity = multipartEntityBuilder.build();
                httppost.setEntity(reqEntity);
                return getResult(httppost);
            } finally {
                in.close();
            }
        } else {
            throw new NullPointerException("上传文件输入流不能为空");
        }
    }

    private static String getResult(HttpRequestBase request) throws Exception {
        config(request);
        // CloseableHttpClient httpclient = HttpClients.createDefault();
        String result = null;
        /*
         * 链接超时
		 * httpClient.getHttpConnectionManager().getParams().setConnectionTimeout
		 * (60000); 读取超时
		 * httpClient.getHttpConnectionManager().getParams().setSoTimeout(60000)
		 * 异常自动恢复 http://www.cnblogs.com/anee/p/3269982.html 请求重试处理:
		 * 为了开启自定义异常恢复机制，应该提供一个HttpRequestRetryHandler接口的实现。
		 */
//		HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
//			@Override
//			public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
//				if (executionCount >= 5) {
//					// 如果超过最大重试次数，那么就不要继续了
//					return false;
//				}
//				if (exception instanceof NoHttpResponseException) {
//					// 如果服务器丢掉了连接，那么就重试
//					return true;
//				} else if (exception instanceof SSLHandshakeException) {
//					// 不要重试SSL握手异常
//					return false;
//				}
//				HttpRequest request = (HttpRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST);
//				boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
//				if (idempotent) {
//					// 如果请求被认为是幂等的，那么就重试
//					return true;
//				}
//				return false;
//			}
//		};

		/*
         * 控制响应的最简便和最方便的方式是使用ResponseHandler接口。这个放完完全减轻了用户关于连接管理的担心。
		 * 当使用ResponseHandler时
		 * ，HttpClient将会自动关注并保证释放连接到连接管理器中去，而不管请求执行是否成功或引发了异常。
		 */
        ResponseHandler<String> handler = new ResponseHandler<String>() {
            @Override
            public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
                StatusLine statusLine = response.getStatusLine();
                if (statusLine.getStatusCode() < HttpStatus.SC_MULTIPLE_CHOICES) {
                    String result = null;
                    HttpEntity entity = response.getEntity();
                    // 显示内容
                    if (entity != null) {
                        result = EntityUtils.toString(entity, "UTF-8");
                        // 打印响应内容
                        System.out.println("Response content: " + result);
                    }
                    return result;
                } else {
                    throw new RuntimeException("HTTP Request is not success, Response code is " + response.getStatusLine().getStatusCode());
                }
            }
        };
        CloseableHttpClient httpclient = HttpConnectionManager.getConnection();
        System.out.println("executing request " + request.getURI());
        result = httpclient.execute(request, handler);
//		try {
//			// 执行get请求.
//			// CloseableHttpResponse response = httpclient.execute(httpRequest);
//		} finally {
//			// 关闭连接,释放资源
//			try {
//				httpclient.close();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//		}
        return result;
    }

    private static HttpResponse getHttpResponse(HttpRequestBase request) throws Exception {
        config(request);
        ResponseHandler<HttpResponse> handler = new ResponseHandler<HttpResponse>() {
            @Override
            public HttpResponse handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
                StatusLine statusLine = response.getStatusLine();
                if (statusLine.getStatusCode() <= HttpStatus.SC_TEMPORARY_REDIRECT) {
                    return response;
                } else {
                    throw new RuntimeException("HTTP Request is not success, Response code is " + response.getStatusLine().getStatusCode());
                }
            }
        };
        CloseableHttpClient httpclient = HttpConnectionManager.getConnection();
        System.out.println("executing request " + request.getURI());
        return httpclient.execute(request, handler);
    }

    /**
     * 将map转化为泛型为BasicNameValuePair的List集合
     *
     * @param map
     * @return
     */
    public static List<NameValuePair> formatNVP(Map map) {
        if (map != null && map.size() > 0) {
            List<NameValuePair> pairs = new ArrayList<NameValuePair>();
            Set<Entry> entries = map.entrySet();
            for (Iterator it = entries.iterator(); it.hasNext(); ) {
                Entry entry = (Entry) it.next();
                Object k = entry.getKey();
                Object v = entry.getValue();
                if (k != null && v != null) {
                    pairs.add(new BasicNameValuePair(k.toString(), v.toString()));
                }
            }
            return pairs;
        }
        return null;
    }

    private static void config(HttpRequestBase httpRequestBase) {
        httpRequestBase.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:6.0.2) Gecko/20100101 Firefox/6.0.2");
        httpRequestBase.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        httpRequestBase.setHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");// "en-US,en;q=0.5");
        // 请求头Accept-Charset 是告诉服务器，客户端提交的表单可能使用的编码。这与服务器的响应内容编码无关。
        // 而响应头Content-Type 中的 charset则将告诉浏览器，服务器返回的文本采用什么编码，这才是问题关键
        httpRequestBase.setHeader("Accept-Charset", "ISO-8859-1,utf-8,gbk,gb2312;q=0.7,*;q=0.7");
        httpRequestBase.setHeader("Accept-Encoding", "gzip, deflate");
        // 配置请求的超时设置
//		RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(3000).setConnectTimeout(3000).setSocketTimeout(3000).build();
//		httpRequestBase.setConfig(requestConfig);
    }

    /**
     * HttpClient连接SSL
     *
     * @return
     * @throws Exception
     */
    protected String ssl() throws Exception {
        CloseableHttpClient httpclient = null;
        String result = null;
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream instream = new FileInputStream(new File("d:\\tomcat.keystore"));
            try {
                // 加载keyStore d:\\tomcat.keystore
                trustStore.load(instream, "123456".toCharArray());
            } catch (CertificateException e) {
                e.printStackTrace();
            } finally {
                try {
                    instream.close();
                } catch (Exception ignore) {
                }
            }
            // 相信自己的CA和所有自签名的证书
            SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
            // 只允许使用TLSv1协议
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            // 创建http请求(get方式)
            HttpGet httpget = new HttpGet("http://www.baidu.com/");
            System.out.println("executing request" + httpget.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                StatusLine statusLine = response.getStatusLine();
                if (statusLine.getStatusCode() < HttpStatus.SC_MULTIPLE_CHOICES) {
                    HttpEntity entity = response.getEntity();
                    // 显示内容
                    if (entity != null) {
                        result = EntityUtils.toString(entity, "UTF-8");
                        System.out.println("Response content length: " + entity.getContentLength());
                        // 打印响应内容
                        System.out.println("Response content: " + result);
                        EntityUtils.consume(entity);
                    }
                } else {
                    throw new Exception("HTTP Request is not success, Response code is " + response.getStatusLine().getStatusCode());
                }
            } finally {
                response.close();
            }
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } finally {
            if (httpclient != null) {
                try {
                    httpclient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

}